#include <iostream>

#include "frpc.h"
#include "router.h"
#include <cppbase/logging.h>

namespace Frpc
{ /*
    Head *Msg::parse_head_meta(char *buf, int size)
    {
        //   DLOG("copy from buf to head");
        read_head(head, buf, sizeof(Head));
        DLOG("to parse meta: total size:%d,head size:8 ,meta size:%d,content_size:%d", size, head->meta_len, head->length);
        auto result = meta.ParseFromArray(buf + sizeof(Head), head->meta_len);

        if (!result)
        {
            printf("failed to parse protobuf bytes\n");
            // set_status_output(Status::BadRequest, Error::UnkonwnMetaData);
            return nullptr;
        }
        return head;
    }
*/
#define Parse_Stage_Init 0
#define Parse_Stage_HeadDone 1
#define Parse_WaitMeta 5
#define Parse_Stage_MetaDone 2
#define Parse_Stage_WaitBody 3
#define Parse_Stage_BodyDone 4
#define Parse_failed 99

    int Msg::parse_full_stream(char *buf, size_t *size)
    {

        DLOG("to parse total size:%ld", *size);
        //  auto head = get_head();
        bool loop = true;
        int used_count = 0;
        while (loop)
        {
            switch (parse_stage)
            {
            case Parse_Stage_Init:
            {
                if (*size < sizeof(Head))
                {
                    parse_stage = Parse_failed;
                }
                else
                {
                    memcpy(head(), buf, sizeof(Head));
                    used_count = sizeof(Head);
                    if (head()->start_bytes != FRPC_START_BYTES)
                    {
                        parse_stage = Parse_failed;
                    }
                    else
                    {
                        parse_stage = Parse_Stage_HeadDone;
                        DLOG("to parse head ok");
                    }
                }
            }
                continue;
            case Parse_Stage_HeadDone:
            case Parse_WaitMeta:
            {
                if (!head()->has_meta())
                {
                    parse_stage = Parse_Stage_MetaDone;
                }
                else
                {
                    uint16_t meta_size = *((uint16_t *)(buf + used_count));
                    
                    DLOG("meta data exist,size:%d", meta_size);
                    if (*size - used_count < meta_size)
                    {
                        parse_stage = Parse_WaitMeta;
                    }
                    else
                    {

                        auto result = meta.ParseFromArray(buf + used_count+2, meta_size);
                        used_count += meta_size+2;
                        if (!result)
                        {
                            parse_stage = Parse_failed;
                        }
                        else
                        {
                            DLOG(" meta parsed ok");
                            parse_stage = Parse_Stage_MetaDone;
                        }
                    }
                }
            }
                continue;

            case Parse_Stage_MetaDone:
            case Parse_Stage_WaitBody:
            {
                int contentlen = head()->content_length();
                if (contentlen == 0)
                {
                    DLOG("content size :0,done");
                    parse_stage = Parse_Stage_BodyDone;
                    continue;
                }

                if (head()->has_meta())
                    contentlen = contentlen - (meta.ByteSizeLong()+2);

                reserve_content_buf(contentlen);
                if (*size - used_count + recieced_contentsize() < contentlen)
                {
                    //FATAL("append partial body done,%ld,%ld,%ld,%ld",*size,used_count,recieced_contentsize(),contentlen);
                    append_input_copy(buf + used_count, *size - used_count);
                    used_count = *size;
                    parse_stage = Parse_Stage_WaitBody;
                    loop = false;
                }
                else
                {

                    int cnt = contentlen - recieced_contentsize();
                    append_input_copy(buf + used_count, cnt);
                    used_count += cnt;
                    parse_stage = Parse_Stage_BodyDone;
                    DLOG("to parse full body done");
                }
            }
                continue;
            case Parse_Stage_BodyDone:
            default:
                loop = false;
                break;
            }
        }
        DLOG("recieved total bytes:%ld,used bytes %d ", *size, used_count);
        *size = used_count;
        DLOG("after parse,stage is:%d", parse_stage);
        switch (parse_stage)
        {
        case Parse_WaitMeta:
        case Parse_Stage_WaitBody:

            return 0;
        case Parse_Stage_BodyDone:
            return 1;
        case Parse_failed:
        default:
            break;
        }
        DLOG("error msg parsing.")
        return -1;
    }
    bool Msg::bad_input()
    {
        return Parse_Stage_BodyDone != parse_stage;
    }

    void Msg::append_input_copy(char *buf, int size)
    {
        
        if (!headcontent_buf.iov_base || headcontent_used > headcontent_buf.iov_len)
        {
            FATAL("you shuold reserve enought memory before append_input_copy");
        }
        memcpy((char *)headcontent_buf.iov_base + headcontent_used, buf, size);
        headcontent_used += size;
    }

    void Msg::set_ok_output(std::string &&s)
    {
        set_cmd_output(Command::StatusOK, std::move(s));
    }
    void Msg::set_ok_output(uint16_t d){
        std::string s;
        s.resize(2);
        *(uint16_t*)s.data()=d;
        set_cmd_output(Command::StatusOK, std::move(s));
    }
    void Msg::set_ok_output(uint32_t d){
        std::string s;
        s.resize(4);
        *(uint32_t*)s.data()=d;
        set_cmd_output(Command::StatusOK, std::move(s));
    }
    void Msg::set_ok_output(uint16_t d,uint16_t d2){
        std::string s;
        s.resize(4);
        *(uint16_t*)s.data()=d;
        *(uint16_t*)(s.data()+2)=d2;
        set_cmd_output(Command::StatusOK, std::move(s));
    }
    void Msg::set_cmd_output(Command cmd, std::string &&s)
    {

        head()->cmd = cmd;
        if (s.size() == 0)
            return;
        if (headcontent_used + s.size() > headcontent_buf.iov_len)
        {
            headcontent_buf.iov_len = headcontent_used + s.size();
            DLOG("cmd(%d) msg expand headcontent_buf.", uint16_t(head()->cmd));
            headcontent_buf.iov_base = realloc(headcontent_buf.iov_base, headcontent_buf.iov_len);
            if (!headcontent_buf.iov_base) //failed to realloc
            {
                FATAL("failed to realloc.");
            }
        }

        memcpy((char *)headcontent_buf.iov_base + headcontent_used, s.data(), s.size());
        headcontent_used += s.size();
    }
    void Msg::set_bad_output(Status code, std::string &&s)
    {   head()->cmd = Command::StatusBad;
        if (headcontent_used + s.size() + sizeof(Status) > headcontent_buf.iov_len)
        {
            headcontent_buf.iov_len = headcontent_used + s.size() + sizeof(Status);
            headcontent_buf.iov_base = realloc(headcontent_buf.iov_base, headcontent_buf.iov_len);
            if (!headcontent_buf.iov_base) //failed to realloc
            {
                FATAL("failed to realloc.");
            }
        }
        *(Status *)((char *)headcontent_buf.iov_base + headcontent_used) = code;
        headcontent_used += sizeof(Status);
        if (s.size() > 0)
        {
            memcpy((char *)headcontent_buf.iov_base + headcontent_used, s.data(), s.size());
            headcontent_used += s.size();
        }
    }
    /*
    void Msg::set_output_copy(char *buf, int size)
    {
        headcontent_buf.iov_len = size;
        if (headcontent_buf.iov_base)
        {
            headcontent_buf.iov_base = realloc(headcontent_buf.iov_base, headcontent_buf.iov_len);
            if (!headcontent_buf.iov_base) //failed to realloc
                std::terminate();
        }
        else
        {
            headcontent_buf.iov_base = malloc(headcontent_buf.iov_len);
        }
        memcpy(headcontent_buf.iov_base, buf, headcontent_buf.iov_len);
    }
    */
    void Msg::add_output_nocopy(void *buf, size_t size)
    {   if(buf)
        attachment.push_back({buf,size});
    }
}
